home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Net / FTP.php < prev    next >
PHP Script  |  2004-10-01  |  70KB  |  2,103 lines

  1. <?php
  2. // +----------------------------------------------------------------------+
  3. // | Net_FTP Version 1.3                                                  |
  4. // +----------------------------------------------------------------------+
  5. // | Copyright (c) 2001-2004 Tobias Schlitt                               |
  6. // +----------------------------------------------------------------------+
  7. // | This source file is subject to version 3.0 of the PHP license,       |
  8. // | that is available at through the world-wide-web at                   |
  9. // | http://www.php.net/license/3_0.txt.                                  |
  10. // | If you did not receive a copy of the PHP license and are unable to   |
  11. // | obtain it through the world-wide-web, please send a note to          |
  12. // | license@php.net so we can mail you a copy immediately.               |
  13. // +----------------------------------------------------------------------+
  14. // | Authors:       Tobias Schlitt <toby@php.net>                         |
  15. // +----------------------------------------------------------------------+
  16. //
  17. // $Id: FTP.php,v 1.32 2004/09/22 17:38:26 toby Exp $
  18.  
  19. require_once 'PEAR.php';
  20.  
  21.  
  22. /**
  23.  * Option to let the ls() method return only files.
  24.  *
  25.  * @since 1.3
  26.  * @name NET_FTP_FILES_ONLY
  27.  * @see Net_FTP::ls()
  28.  */
  29. define('NET_FTP_FILES_ONLY', 0, true);
  30.  
  31. /**
  32.  * Option to let the ls() method return only directories.
  33.  *
  34.  * @since 1.3
  35.  * @name NET_FTP_DIRS_ONLY
  36.  * @see Net_FTP::ls()
  37.  */
  38. define('NET_FTP_DIRS_ONLY', 1, true);
  39.  
  40. /**
  41.  * Option to let the ls() method return directories and files (default).
  42.  *
  43.  * @since 1.3
  44.  * @name NET_FTP_DIRS_FILES
  45.  * @see Net_FTP::ls()
  46.  */
  47. define('NET_FTP_DIRS_FILES', 2, true);
  48.  
  49. /**
  50.  * Option to let the ls() method return the raw directory listing from ftp_rawlist().
  51.  *
  52.  * @since 1.3
  53.  * @name NET_FTP_RAWLIST
  54.  * @see Net_FTP::ls()
  55.  */
  56. define('NET_FTP_RAWLIST', 3, true);
  57.  
  58.  
  59. /**
  60.  * Error code to indicate a failed connection
  61.  * This error code indicates, that the connection you tryed to set up
  62.  * could not be established. Check your connection settings (host & port)!
  63.  *
  64.  * @since 1.3
  65.  * @name NET_FTP_ERR_CONNECT_FAILED
  66.  * @see Net_FTP::connect()
  67.  */
  68. define('NET_FTP_ERR_CONNECT_FAILED', -1);
  69.  
  70. /**
  71.  * Error code to indicate a failed login
  72.  * This error code indicates, that the login to the FTP server failed. Check
  73.  * your user data (username & password).
  74.  *
  75.  * @since 1.3
  76.  * @name NET_FTP_ERR_LOGIN_FAILED
  77.  * @see Net_FTP::login()
  78.  */
  79. define('NET_FTP_ERR_LOGIN_FAILED', -2);
  80.  
  81. /**
  82.  * Error code to indicate a failed directory change
  83.  * The cd() method failed. Ensure that the directory you wanted to access exists.
  84.  *
  85.  * @since 1.3
  86.  * @name NET_FTP_ERR_DIRCHANGE_FAILED
  87.  * @see Net_FTP::cd()
  88.  */
  89. define('NET_FTP_ERR_DIRCHANGE_FAILED', 2); // Compatibillity reasons!
  90.  
  91. /**
  92.  * Error code to indicate that Net_FTP could not determine the current path
  93.  * The cwd() method failed and could not determine the path you currently reside
  94.  * in on the FTP server.
  95.  *
  96.  * @since 1.3
  97.  * @name NET_FTP_ERR_DETERMINEPATH_FAILED
  98.  * @see Net_FTP::pwd()
  99.  */
  100. define('NET_FTP_ERR_DETERMINEPATH_FAILED', 4); // Compatibillity reasons!
  101.  
  102. /**
  103.  * Error code to indicate that the creation of a directory failed
  104.  * The directory you tryed to create could not be created. Check the
  105.  * access rights on the parent directory!
  106.  *
  107.  * @since 1.3
  108.  * @name NET_FTP_ERR_CREATEDIR_FAILED
  109.  * @see Net_FTP::mkdir()
  110.  */
  111. define('NET_FTP_ERR_CREATEDIR_FAILED', -4);
  112.  
  113. /**
  114.  * Error code to indicate that the EXEC execution failed.
  115.  * The execution of a command using EXEC failed. Ensure, that your
  116.  * FTP server supports the EXEC command.
  117.  *
  118.  * @since 1.3
  119.  * @name NET_FTP_ERR_EXEC_FAILED
  120.  * @see Net_FTP::execute()
  121.  */
  122. define('NET_FTP_ERR_EXEC_FAILED', -5);
  123.  
  124. /**
  125.  * Error code to indicate that the SITE command failed.
  126.  * The execution of a command using SITE failed. Ensure, that your
  127.  * FTP server supports the SITE command.
  128.  *
  129.  * @since 1.3
  130.  * @name NET_FTP_ERR_SITE_FAILED
  131.  * @see Net_FTP::site()
  132.  */
  133. define('NET_FTP_ERR_SITE_FAILED', -6);
  134.  
  135. /**
  136.  * Error code to indicate that the CHMOD command failed.
  137.  * The execution of CHMOD failed. Ensure, that your
  138.  * FTP server supports the CHMOD command and that you have the appropriate
  139.  * access rights to use CHMOD.
  140.  *
  141.  * @since 1.3
  142.  * @name NET_FTP_ERR_CHMOD_FAILED
  143.  * @see Net_FTP::chmod()
  144.  */
  145. define('NET_FTP_ERR_CHMOD_FAILED', -7);
  146.  
  147. /**
  148.  * Error code to indicate that a file rename failed
  149.  * The renaming of a file on the server failed. Ensure that you have the
  150.  * appropriate access rights to rename the file.
  151.  *
  152.  * @since 1.3
  153.  * @name NET_FTP_ERR_RENAME_FAILED
  154.  * @see Net_FTP::rename()
  155.  */
  156. define('NET_FTP_ERR_RENAME_FAILED', -8);
  157.  
  158. /**
  159.  * Error code to indicate that the MDTM command failed
  160.  * The MDTM command is not supported for directories. Ensure that you gave
  161.  * a file path to the mdtm() method, not a directory path.
  162.  *
  163.  * @since 1.3
  164.  * @name NET_FTP_ERR_MDTMDIR_UNSUPPORTED
  165.  * @see Net_FTP::mdtm()
  166.  */
  167. define('NET_FTP_ERR_MDTMDIR_UNSUPPORTED', -9);
  168.  
  169. /**
  170.  * Error code to indicate that the MDTM command failed
  171.  * The MDTM command failed. Ensure that your server supports the MDTM command.
  172.  *
  173.  * @since 1.3
  174.  * @name NET_FTP_ERR_MDTM_FAILED
  175.  * @see Net_FTP::mdtm()
  176.  */
  177. define('NET_FTP_ERR_MDTM_FAILED', -10);
  178.  
  179. /**
  180.  * Error code to indicate that a date returned by the server was misformated
  181.  * A date string returned by your server seems to be missformated and could not be
  182.  * parsed. Check that the server is configured correctly. If you're sure, please
  183.  * send an email to the auhtor with a dumped output of $ftp->ls('./', NET_FTP_RAWLIST);
  184.  * to get the date format supported.
  185.  *
  186.  * @since 1.3
  187.  * @name NET_FTP_ERR_DATEFORMAT_FAILED
  188.  * @see Net_FTP::mdtm(), Net_FTP::ls()
  189.  */
  190. define('NET_FTP_ERR_DATEFORMAT_FAILED', -11);
  191.  
  192. /**
  193.  * Error code to indicate that the SIZE command failed
  194.  * The determination of the filesize of a file failed. Ensure that your server supports the
  195.  * SIZE command.
  196.  *
  197.  * @since 1.3
  198.  * @name NET_FTP_ERR_SIZE_FAILED
  199.  * @see Net_FTP::size()
  200.  */
  201. define('NET_FTP_ERR_SIZE_FAILED', -12);
  202.  
  203. /**
  204.  * Error code to indicate that a local file could not be overwritten
  205.  * You specified not to overwrite files. Therefore the local file has not been
  206.  * overwriten. If you want to get the file overwriten, please set the option to
  207.  * do so.
  208.  *
  209.  * @since 1.3
  210.  * @name NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN
  211.  * @see Net_FTP::get(), Net_FTP::getRecursive()
  212.  */
  213. define('NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN', -13);
  214.  
  215. /**
  216.  * Error code to indicate that a local file could not be overwritten
  217.  * Also you specified to overwrite the local file you want to download to,
  218.  * it has not been possible to do so. Check that you have the appropriate access
  219.  * rights on the local file to overwrite it.
  220.  *
  221.  * @since 1.3
  222.  * @name NET_FTP_ERR_OVERWRITELOCALFILE_FAILED
  223.  * @see Net_FTP::get(), Net_FTP::getRecursive()
  224.  */
  225. define('NET_FTP_ERR_OVERWRITELOCALFILE_FAILED', -14);
  226.  
  227. /**
  228.  * Error code to indicate that the file you wanted to upload does not exist
  229.  * The file you tried to upload does not exist. Ensure that it exists.
  230.  *
  231.  * @since 1.3
  232.  * @name NET_FTP_ERR_LOCALFILENOTEXIST
  233.  * @see Net_FTP::put(), Net_FTP::putRecursive()
  234.  */
  235. define('NET_FTP_ERR_LOCALFILENOTEXIST', -15);
  236.  
  237. /**
  238.  * Error code to indicate that a remote file could not be overwritten
  239.  * You specified not to overwrite files. Therefore the remote file has not been
  240.  * overwriten. If you want to get the file overwriten, please set the option to
  241.  * do so.
  242.  *
  243.  * @since 1.3
  244.  * @name NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN
  245.  * @see Net_FTP::put(), Net_FTP::putRecursive()
  246.  */
  247. define('NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN', -16);
  248.  
  249. /**
  250.  * Error code to indicate that the upload of a file failed
  251.  * The upload you tried failed. Ensure that you have appropriate access rights
  252.  * to upload the desired file.
  253.  *
  254.  * @since 1.3
  255.  * @name NET_FTP_ERR_UPLOADFILE_FAILED
  256.  * @see Net_FTP::put(), Net_FTP::putRecursive()
  257.  */
  258. define('NET_FTP_ERR_UPLOADFILE_FAILED', -17);
  259.  
  260. /**
  261.  * Error code to indicate that you specified an incorrect directory path
  262.  * The remote path you specified seems not to be a directory. Ensure that
  263.  * the path you specify is a directory and that the path string ends with
  264.  * a /.
  265.  *
  266.  * @since 1.3
  267.  * @name NET_FTP_ERR_REMOTEPATHNODIR
  268.  * @see Net_FTP::putRecursive(), Net_FTP::getRecursive()
  269.  */
  270. define('NET_FTP_ERR_REMOTEPATHNODIR', -18);
  271.  
  272. /**
  273.  * Error code to indicate that you specified an incorrect directory path
  274.  * The local path you specified seems not to be a directory. Ensure that
  275.  * the path you specify is a directory and that the path string ends with
  276.  * a /.
  277.  *
  278.  * @since 1.3
  279.  * @name NET_FTP_ERR_LOCALPATHNODIR
  280.  * @see Net_FTP::putRecursive(), Net_FTP::getRecursive()
  281.  */
  282. define('NET_FTP_ERR_LOCALPATHNODIR', -19);
  283.  
  284. /**
  285.  * Error code to indicate that a local directory failed to be created
  286.  * You tried to create a local directory through getRecursive() method,
  287.  * which has failed. Ensure that you have the appropriate access rights
  288.  * to create it.
  289.  *
  290.  * @since 1.3
  291.  * @name NET_FTP_ERR_CREATELOCALDIR_FAILED
  292.  * @see Net_FTP::getRecursive()
  293.  */
  294. define('NET_FTP_ERR_CREATELOCALDIR_FAILED', -20);
  295.  
  296. /**
  297.  * Error code to indicate that the provided hostname was incorrect
  298.  * The hostname you provided was invalid. Ensure to provide either a
  299.  * full qualified domain name or an IP address.
  300.  *
  301.  * @since 1.3
  302.  * @name NET_FTP_ERR_HOSTNAMENOSTRING
  303.  * @see Net_FTP::setHostname()
  304.  */
  305. define('NET_FTP_ERR_HOSTNAMENOSTRING', -21);
  306.  
  307. /**
  308.  * Error code to indicate that the provided port was incorrect
  309.  * The port number you provided was invalid. Ensure to provide either a
  310.  * a numeric port number greater zero.
  311.  *
  312.  * @since 1.3
  313.  * @name NET_FTP_ERR_PORTLESSZERO
  314.  * @see Net_FTP::setPort()
  315.  */
  316. define('NET_FTP_ERR_PORTLESSZERO', -22);
  317.  
  318. /**
  319.  * Error code to indicate that you provided an invalid mode constant
  320.  * The mode constant you provided was invalid. You may only provide
  321.  * FTP_ASCII or FTP_BINARY.
  322.  *
  323.  * @since 1.3
  324.  * @name NET_FTP_ERR_NOMODECONST
  325.  * @see Net_FTP::setMode()
  326.  */
  327. define('NET_FTP_ERR_NOMODECONST', -23);
  328.  
  329. /**
  330.  * Error code to indicate that you provided an invalid timeout
  331.  * The timeout you provided was invalid. You have to provide a timeout greater
  332.  * or equal to zero.
  333.  *
  334.  * @since 1.3
  335.  * @name NET_FTP_ERR_TIMEOUTLESSZERO
  336.  * @see Net_FTP::Net_FTP(), Net_FTP::setTimeout()
  337.  */
  338. define('NET_FTP_ERR_TIMEOUTLESSZERO', -24);
  339.  
  340. /**
  341.  * Error code to indicate that you provided an invalid timeout
  342.  * An error occured while setting the timeout. Ensure that you provide a
  343.  * valid integer for the timeount and that your PHP installation works
  344.  * correctly.
  345.  *
  346.  * @since 1.3
  347.  * @name NET_FTP_ERR_SETTIMEOUT_FAILED
  348.  * @see Net_FTP::Net_FTP(), Net_FTP::setTimeout()
  349.  */
  350. define('NET_FTP_ERR_SETTIMEOUT_FAILED', -25);
  351.  
  352. /**
  353.  * Error code to indicate that the provided extension file doesn't exist
  354.  * The provided extension file does not exist. Ensure to provided an
  355.  * existant extension file.
  356.  *
  357.  * @since 1.3
  358.  * @name NET_FTP_ERR_EXTFILENOTEXIST
  359.  * @see Net_FTP::getExtensionFile()
  360.  */
  361. define('NET_FTP_ERR_EXTFILENOTEXIST', -26);
  362.  
  363. /**
  364.  * Error code to indicate that the provided extension file is not readable
  365.  * The provided extension file is not readable. Ensure to have sufficient
  366.  * access rights for it.
  367.  *
  368.  * @since 1.3
  369.  * @name NET_FTP_ERR_EXTFILEREAD_FAILED
  370.  * @see Net_FTP::getExtensionFile()
  371.  */
  372. define('NET_FTP_ERR_EXTFILEREAD_FAILED', -27);
  373.  
  374. /**
  375.  * Error code to indicate that the deletion of a file failed
  376.  * The specified file could not be deleted. Ensure to have sufficient
  377.  * access rights to delete the file.
  378.  *
  379.  * @since 1.3
  380.  * @name NET_FTP_ERR_EXTFILEREAD_FAILED
  381.  * @see Net_FTP::rm()
  382.  */
  383. define('NET_FTP_ERR_DELETEFILE_FAILED', -28);
  384.  
  385. /**
  386.  * Error code to indicate that the deletion of a directory faild
  387.  * The specified file could not be deleted. Ensure to have sufficient
  388.  * access rights to delete the file.
  389.  *
  390.  * @since 1.3
  391.  * @name NET_FTP_ERR_EXTFILEREAD_FAILED
  392.  * @see Net_FTP::rm()
  393.  */
  394. define('NET_FTP_ERR_DELETEDIR_FAILED', -29);
  395.  
  396. /**
  397.  * Error code to indicate that the directory listing failed
  398.  * PHP could not list the directory contents on the server. Ensure
  399.  * that your server is configured appropriate.
  400.  *
  401.  * @since 1.3
  402.  * @name NET_FTP_ERR_RAWDIRLIST_FAILED
  403.  * @see Net_FTP::ls()
  404.  */
  405. define('NET_FTP_ERR_RAWDIRLIST_FAILED', -30);
  406.  
  407. /**
  408.  * Error code to indicate that the directory listing failed
  409.  * The directory listing format your server uses seems not to
  410.  * be supported by Net_FTP. Please send the output of the
  411.  * call ls('./', NET_FTP_RAWLIST); to the author of this
  412.  * class to get it supported.
  413.  *
  414.  * @since 1.3
  415.  * @name NET_FTP_ERR_DIRLIST_UNSUPPORTED
  416.  * @see Net_FTP::ls()
  417.  */
  418. define('NET_FTP_ERR_DIRLIST_UNSUPPORTED', -31);
  419.  
  420. /**
  421.  * Error code to indicate failed disconnecting
  422.  * This error code indicates, that disconnection was not possible.
  423.  *
  424.  * @since 1.3
  425.  * @name NET_FTP_ERR_DISCONNECT_FAILED
  426.  * @see Net_FTP::disconnect()
  427.  */
  428. define('NET_FTP_ERR_DISCONNECT_FAILED', -32);
  429.  
  430. /**
  431.  * Error code to indicate that the username you provided was invalid.
  432.  * Check that you provided a non-empty string as the username.
  433.  *
  434.  * @since 1.3
  435.  * @name NET_FTP_ERR_USERNAMENOSTRING
  436.  * @see Net_FTP::setUsername()
  437.  */
  438. define('NET_FTP_ERR_USERNAMENOSTRING', -33);
  439.  
  440. /**
  441.  * Error code to indicate that the username you provided was invalid.
  442.  * Check that you provided a non-empty string as the username.
  443.  *
  444.  * @since 1.3
  445.  * @name NET_FTP_ERR_PASSWORDNOSTRING
  446.  * @see Net_FTP::setPassword()
  447.  */
  448. define('NET_FTP_ERR_PASSWORDNOSTRING', -33);
  449.  
  450.  
  451. /**
  452.  * Class for comfortable FTP-communication
  453.  *
  454.  * This class provides comfortable communication with FTP-servers. You may do everything
  455.  * enabled by the PHP-FTP-extension and further functionalities, like recursive-deletion,
  456.  * -up- and -download. Another feature is to create directories recursively.
  457.  *
  458.  * @since     PHP 4.2.3
  459.  * @author    Tobias Schlitt <toby@php.net>
  460.  * @see       http://www.schlitt.info
  461.  * @license   http://www.php.net/license/3_0.txt  PHP License 3.0
  462.  */
  463.  
  464. class Net_FTP extends PEAR 
  465. {
  466.     /**
  467.      * The host to connect to
  468.      *
  469.      * @access  private
  470.      * @var     string
  471.      */
  472.     
  473.     var $_hostname;
  474.  
  475.     /**
  476.      * The port for ftp-connection (standard is 21)
  477.      *
  478.      * @access  private
  479.      * @var     int
  480.      */
  481.     
  482.     var $_port = 21;
  483.  
  484.     /**
  485.      * The username for login
  486.      *
  487.      * @access  private
  488.      * @var     string
  489.      */
  490.     
  491.     var $_username;
  492.  
  493.     /**
  494.      * The password for login
  495.      *
  496.      * @access  private
  497.      * @var     string
  498.      */
  499.     
  500.     var $_password;
  501.  
  502.     /**
  503.      * Determine whether to use passive-mode (true) or active-mode (false)
  504.      *
  505.      * @access  private
  506.      * @var     bool
  507.      */
  508.     
  509.     var $_passv;
  510.  
  511.     /**
  512.      * The standard mode for ftp-transfer
  513.      *
  514.      * @access  private
  515.      * @var     int
  516.      */
  517.     
  518.     var $_mode = FTP_BINARY;
  519.  
  520.     /**
  521.      * This holds the handle for the ftp-connection
  522.      *
  523.      * @access  private
  524.      * @var     resource
  525.      */
  526.     
  527.     var $_handle;
  528.  
  529.     /**
  530.      * Contains the timeout for FTP operations
  531.      *
  532.      * @access  private
  533.      * @var     int
  534.      * @since   1.3
  535.      */
  536.     
  537.     var $_timeout = 90;
  538.         
  539.     /**
  540.      * Saves file-extensions for ascii- and binary-mode
  541.      *
  542.      * The array contains 2 sub-arrays ("ascii" and "binary"), which both contain
  543.      * file-extensions without the "." (".php" = "php").
  544.      *
  545.      * @access  private
  546.      * @var     array
  547.      */
  548.     
  549.     var $_file_extensions;
  550.  
  551.     /**
  552.      * ls match
  553.      * Matches the ls entries against a regex and maps the resulting array to speaking names
  554.      *
  555.      * @access  private
  556.      * @var     array
  557.      * @since   1.3
  558.      */
  559.     
  560.     var $_ls_match = array(
  561.         'unix'    => array(
  562.             'pattern' => '/(?:(d)|.)([rwxt-]+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\S+\s+\S+\s+\S+)\s+(.+)/',
  563.             'map'     => array('name'=>8,'size'=>6,'rights'=>2,'user'=>4,'group'=>5,
  564.                               'files_inside'=>3,'date'=>7,'is_dir'=>1)
  565.         ),
  566.         'windows' => array(
  567.             'pattern' => '/(.+)\s+(.+)\s+((<DIR>)|[0-9]+)\s+(.+)/',
  568.             'map'     => array('name'=>5,'date'=>1,'is_dir'=>3)
  569.         )
  570.     );
  571.     
  572.     /**
  573.      * matcher
  574.      * Stores the matcher for the current connection
  575.      *
  576.      * @access  private
  577.      * @var     array
  578.      * @since   1.3
  579.      */
  580.     var $_matcher = null;
  581.     
  582.     /**
  583.      * Holds all Net_FTP_Observer objects 
  584.      * that wish to be notified of new messages.
  585.      *
  586.      * @var     array
  587.      * @access  private
  588.      * @since   1.3
  589.      */
  590.     
  591.     var $_listeners = array();
  592.  
  593.     /**
  594.      * This generates a new FTP-Object. The FTP-connection will not be established, yet.
  595.      * You can leave $host and $port blank, if you want. The $host will not be set
  596.      * and the $port will be left at 21. You have to set the $host manualy before
  597.      * trying to connect or with the connect() method.
  598.      *
  599.      * @access  public
  600.      * @param   string $host    (optional) The hostname 
  601.      * @param   int    $port    (optional) The port
  602.      * @param   int    $timeout (optional) Sets the standard timeout
  603.      * @return  void
  604.      * @see     Net_FTP::setHostname(), Net_FTP::setPort(), Net_FTP::connect()
  605.      */
  606.     
  607.     function Net_FTP($host = null, $port = null, $timeout = 90)
  608.     {
  609.         $this->PEAR();
  610.         if (isset($host)) {
  611.             $this->setHostname($host);
  612.         }
  613.         if (isset($port)) {
  614.             $this->setPort($port);
  615.         }
  616.         $this->_timeout = $timeout;
  617.         $this->_file_extensions[FTP_ASCII] = array();
  618.         $this->_file_extensions[FTP_BINARY] = array();
  619.     }
  620.  
  621.     /**
  622.      * This function generates the FTP-connection. You can optionally define a
  623.      * hostname and/or a port. If you do so, this data is stored inside the object.
  624.      *
  625.      * @access  public
  626.      * @param   string $host    (optional) The Hostname
  627.      * @param   int    $port    (optional) The Port 
  628.      * @return  mixed           True on success, otherwise PEAR::Error
  629.      * @see     NET_FTP_ERR_CONNECT_FAILED
  630.      */
  631.     
  632.     function connect($host = null, $port = null)
  633.     {
  634.         $this->_matcher = null;
  635.         if (isset($host)) {
  636.             $this->setHostname($host);
  637.         }
  638.         if (isset($port)) {
  639.             $this->setPort($port);
  640.         }
  641.         $handle = @ftp_connect($this->getHostname(), $this->getPort(), $this->_timeout);
  642.         if (!$handle) {
  643.             return $this->raiseError("Connection to host failed", NET_FTP_ERR_CONNECT_FAILED);
  644.         } else {
  645.             $this->_handle =& $handle;
  646.             return true;
  647.         }
  648.     }
  649.  
  650.     /**
  651.      * This function close the FTP-connection
  652.      *
  653.      * @access  public
  654.      * @return  bool|PEAR_Error Returns true on success, PEAR_Error on failure
  655.      */
  656.     
  657.     function disconnect()
  658.     {
  659.         $res = @ftp_close($this->_handle);
  660.         if (!$res) {
  661.             return PEAR::raiseError('Disconnect failed.', NET_FTP_ERR_DISCONNECT_FAILED);
  662.         }
  663.         return true;
  664.     }
  665.  
  666.     /**
  667.      * This logges you into the ftp-server. You are free to specify username and password
  668.      * in this method. If you specify it, the values will be taken into the corresponding
  669.      * attributes, if do not specify, the attributes are taken.
  670.      *
  671.      * @access  public
  672.      * @param   string $username  (optional) The username to use 
  673.      * @param   string $password  (optional) The password to use
  674.      * @return  mixed             True on success, otherwise PEAR::Error
  675.      * @see     NET_FTP_ERR_LOGIN_FAILED
  676.      */
  677.     
  678.     function login($username = null, $password = null)
  679.     {
  680.         if (!isset($username)) {
  681.             $username = $this->getUsername();
  682.         } else {
  683.             $this->setUsername($username);
  684.         }
  685.  
  686.         if (!isset($password)) {
  687.             $password = $this->getPassword();
  688.         } else {
  689.             $this->setPassword($password);
  690.         }
  691.  
  692.         $res = @ftp_login($this->_handle, $username, $password);
  693.  
  694.         if (!$res) {
  695.             return $this->raiseError("Unable to login", NET_FTP_ERR_LOGIN_FAILED);
  696.         } else {
  697.             return true;
  698.         }
  699.     }
  700.  
  701.     /**
  702.      * This changes the currently used directory. You can use either an absolute
  703.      * directory-path (e.g. "/home/blah") or a relative one (e.g. "../test").
  704.      *
  705.      * @access  public
  706.      * @param   string $dir  The directory to go to.
  707.      * @return  mixed        True on success, otherwise PEAR::Error
  708.      * @see     NET_FTP_ERR_DIRCHANGE_FAILED
  709.      */
  710.     
  711.     function cd($dir)
  712.     {
  713.         $erg = @ftp_chdir($this->_handle, $dir);
  714.         if (!$erg) {
  715.             return $this->raiseError("Directory change failed", NET_FTP_ERR_DIRCHANGE_FAILED);
  716.         } else {
  717.             return true;
  718.         }
  719.     }
  720.  
  721.     /**
  722.      * Show's you the actual path on the server
  723.      * This function questions the ftp-handle for the actual selected path and returns it.
  724.      *
  725.      * @access  public
  726.      * @return  mixed        The actual path or PEAR::Error
  727.      * @see     NET_FTP_ERR_DETERMINEPATH_FAILED
  728.      */
  729.     
  730.     function pwd()
  731.     {
  732.         $res = @ftp_pwd($this->_handle);
  733.         if (!$res) {
  734.             return $this->raiseError("Could not determine the actual path.", NET_FTP_ERR_DETERMINEPATH_FAILED);
  735.         } else {
  736.             return $res;
  737.         }
  738.     }
  739.  
  740.     /**
  741.      * This works similar to the mkdir-command on your local machine. You can either give
  742.      * it an absolute or relative path. The relative path will be completed with the actual
  743.      * selected server-path. (see: pwd())
  744.      *
  745.      * @access  public
  746.      * @param   string $dir       Absolute or relative dir-path
  747.      * @param   bool   $recursive (optional) Create all needed directories
  748.      * @return  mixed             True on success, otherwise PEAR::Error
  749.      * @see     NET_FTP_ERR_CREATEDIR_FAILED
  750.      */
  751.     
  752.     function mkdir($dir, $recursive = false)
  753.     {
  754.         $dir = $this->_construct_path($dir);
  755.         if ($recursive === false){
  756.             $res = @ftp_mkdir($this->_handle, $dir);
  757.             if (!$res) {
  758.                 return $this->raiseError("Creation of '$dir' failed", NET_FTP_ERR_CREATEDIR_FAILED);
  759.             } else {
  760.                 return true;
  761.             }
  762.         } else {
  763.             $pos = 0;
  764.             if(strpos($dir, '/') === false) {
  765.                 return $this->mkdir($dir,false);
  766.             }
  767.             $elements = array();
  768.             while (false !== ($pos = strpos($dir, '/', $pos + 1))){
  769.                 $elements[] = substr($dir, 0, $pos);
  770.             }
  771.             foreach ($elements as $element){
  772.                 $res = $this->mkdir($element, false);
  773.                 if($res !== true) {
  774.                     return $res;
  775.                 }
  776.             }
  777.             return true;
  778.         }
  779.     }
  780.  
  781.     /**
  782.      * This method tries executing a command on the ftp, using SITE EXEC.
  783.      *
  784.      * @access  public
  785.      * @param   string $command The command to execute
  786.      * @return  mixed           The result of the command (if successfull), otherwise PEAR::Error
  787.      * @see     NET_FTP_ERR_EXEC_FAILED
  788.      */
  789.     
  790.     function execute($command)
  791.     {
  792.         $res = @ftp_exec($this->_handle, $command);
  793.         if (!$res) {
  794.             return $this->raiseError("Execution of command '$command' failed.", NET_FTP_ERR_EXEC_FAILED);
  795.         } else {
  796.             return $res;
  797.         }
  798.     }
  799.  
  800.     /**
  801.      * Execute a SITE command on the server
  802.      * This method tries to execute a SITE command on the ftp server.
  803.      *
  804.      * @access  public
  805.      * @param   string $command   The command with parameters to execute
  806.      * @return  mixed             True if successful, otherwise PEAR::Error
  807.      * @see     NET_FTP_ERR_SITE_FAILED
  808.      */
  809.     
  810.     function site($command)
  811.     {
  812.         $res = @ftp_site($this->_handle, $command);
  813.         if (!$res) {
  814.             return $this->raiseError("Execution of SITE command '$command' failed.", NET_FTP_ERR_SITE_FAILED);
  815.         } else {
  816.             return $res;
  817.         }
  818.     }
  819.  
  820.     /**
  821.      * This method will try to chmod the file specified on the server
  822.      * Currently, you must give a number as the the permission argument (777 or
  823.      * similar). The file can be either a relative or absolute path.
  824.      * NOTE: Some servers do not support this feature. In that case, you will
  825.      * get a PEAR error object returned. If successful, the method returns true
  826.      *
  827.      * @access  public
  828.      * @param   mixed   $target        The file or array of files to set permissions for
  829.      * @param   integer $permissions   The mode to set the file permissions to
  830.      * @return  mixed                  True if successful, otherwise PEAR::Error
  831.      * @see     NET_FTP_ERR_CHMOD_FAILED
  832.      */
  833.     
  834.     function chmod($target, $permissions)
  835.     {
  836.         // If $target is an array: Loop through it.
  837.         if (is_array($target)) {
  838.  
  839.             for ($i = 0; $i < count($target); $i++) {
  840.                 $res = $this->chmod($target[$i], $permissions);
  841.                 if (PEAR::isError($res)) {
  842.                     return $res;
  843.                 } // end if isError
  844.             } // end for i < count($target)
  845.  
  846.         } else {
  847.  
  848.             $res = $this->site("CHMOD " . $permissions . " " . $target);
  849.             if (!$res) {
  850.                 return PEAR::raiseError("CHMOD " . $permissions . " " . $target . " failed", NET_FTP_ERR_CHMOD_FAILED);
  851.             } else {
  852.                 return $res;
  853.             }
  854.  
  855.         } // end if is_array
  856.  
  857.     } // end method chmod
  858.  
  859.     /**
  860.      * This method will try to chmod a folder and all of its contents
  861.      * on the server. The target argument must be a folder or an array of folders
  862.      * and the permissions argument have to be an integer (i.e. 777).
  863.      * The file can be either a relative or absolute path.
  864.      * NOTE: Some servers do not support this feature. In that case, you
  865.      * will get a PEAR error object returned. If successful, the method
  866.      * returns true
  867.      *
  868.      * @access  public
  869.      * @param   mixed   $target        The folder or array of folders to
  870.      *                                 set permissions for
  871.      * @param   integer $permissions   The mode to set the folder
  872.      *                                 and file permissions to
  873.      * @return  mixed                  True if successful, otherwise PEAR::Error
  874.      * @see     NET_FTP_ERR_CHMOD_FAILED, NET_FTP_ERR_DETERMINEPATH_FAILED, NET_FTP_ERR_RAWDIRLIST_FAILED, NET_FTP_ERR_DIRLIST_UNSUPPORTED
  875.      */
  876.     
  877.     function chmodRecursive($target, $permissions)
  878.     {
  879.         static $dir_permissions;
  880.  
  881.         if(!isset($dir_permissions)){ // Making directory specific permissions
  882.             $dir_permissions = $this->_makeDirPermissions($permissions);
  883.         }
  884.  
  885.         // If $target is an array: Loop through it
  886.         if (is_array($target)) {
  887.  
  888.             for ($i = 0; $i < count($target); $i++) {
  889.                 $res = $this->chmodRecursive($target[$i], $permissions);
  890.                 if (PEAR::isError($res)) {
  891.                     return $res;
  892.                 } // end if isError
  893.             } // end for i < count($target)
  894.  
  895.         } else {
  896.  
  897.             $remote_path = $this->_construct_path($target);
  898.  
  899.             // Chmod the directory itself
  900.             $result = $this->chmod($remote_path, $dir_permissions);
  901.  
  902.             if (PEAR::isError($result)) {
  903.                 return $result;
  904.             }
  905.  
  906.             // If $remote_path last character is not a slash, add one
  907.             if (substr($remote_path, strlen($remote_path)-1) != "/") {
  908.  
  909.                 $remote_path .= "/";
  910.             }
  911.  
  912.             $dir_list = array();
  913.             $mode = NET_FTP_DIRS_ONLY;
  914.             $dir_list = $this->ls($remote_path, $mode);
  915.             foreach ($dir_list as $dir_entry) {
  916.  
  917.                 $remote_path_new = $remote_path.$dir_entry["name"]."/";
  918.  
  919.                 // Chmod the directory we're about to enter
  920.                 $result = $this->chmod($remote_path_new, $dir_permissions);
  921.  
  922.                 if (PEAR::isError($result)) {
  923.                     return $result;
  924.                 }
  925.  
  926.                 $result = $this->chmodRecursive($remote_path_new, $permissions);
  927.  
  928.                 if (PEAR::isError($result)) {
  929.                     return $result;
  930.                 }
  931.  
  932.             } // end foreach dir_list as dir_entry
  933.  
  934.             $file_list = array();
  935.             $mode = NET_FTP_FILES_ONLY;
  936.             $file_list = $this->ls($remote_path, $mode);
  937.  
  938.             foreach ($file_list as $file_entry) {
  939.  
  940.                 $remote_file = $remote_path.$file_entry["name"];
  941.  
  942.                 $result = $this->chmod($remote_file, $permissions);
  943.  
  944.                 if (PEAR::isError($result)) {
  945.                     return $result;
  946.                 }
  947.  
  948.             } // end foreach $file_list
  949.  
  950.         } // end if is_array
  951.  
  952.         return true; // No errors
  953.  
  954.     } // end method chmodRecursive
  955.  
  956.     /**
  957.      * Rename or move a file or a directory from the ftp-server
  958.      *
  959.      * @access  public
  960.      * @param   string $remote_from The remote file or directory original to rename or move
  961.      * @param   string $remote_to The remote file or directory final to rename or move
  962.      * @return  bool $res True on success, otherwise PEAR::Error
  963.      * @see     NET_FTP_ERR_RENAME_FAILED
  964.      */
  965.  
  966.     function rename ($remote_from, $remote_to) 
  967.     {
  968.         $res = @ftp_rename($this->_handle, $remote_from, $remote_to);
  969.         if(!$res) {
  970.             return $this->raiseError("Could not rename ".$remote_from." to ".$remote_to." !", NET_FTP_ERR_RENAME_FAILED);
  971.         }
  972.         return true;
  973.     }
  974.  
  975.     /**
  976.      * This will return logical permissions mask for directory.
  977.      * if directory have to be writeable it have also be executable
  978.      *
  979.      * @access  private
  980.      * @param   string $permissions    File permissions in digits for file (i.e. 666)
  981.      * @return  string                 File permissions in digits for directory (i.e. 777)
  982.      */
  983.  
  984.     function _makeDirPermissions($permissions){
  985.         $permissions = (string)$permissions;
  986.  
  987.         for($i = 0; $i < strlen($permissions); $i++){ // going through (user, group, world)
  988.             if((int)$permissions{$i} & 4 and !((int)$permissions{$i} & 1)){ // Read permission is set
  989.                                                                             // but execute not yet
  990.                 (int)$permissions{$i} = (int)$permissions{$i} + 1; // Adding execute flag
  991.             }
  992.         }
  993.  
  994.         return (string)$permissions;
  995.     }
  996.  
  997.     /**
  998.      * This will return the last modification-time of a file. You can either give this
  999.      * function a relative or an absolute path to the file to check.
  1000.      * NOTE: Some servers will not support this feature and the function works
  1001.      * only on files, not directories! When successful,
  1002.      * it will return the last modification-time as a unix-timestamp or, when $format is
  1003.      * specified, a preformated timestring.
  1004.      *
  1005.      * @access  public
  1006.      * @param   string $file    The file to check
  1007.      * @param   string $format  (optional) The format to give the date back 
  1008.      *                          if not set, it will return a Unix timestamp
  1009.      * @return  mixed           Unix timestamp, a preformated date-string or PEAR::Error
  1010.      * @see     NET_FTP_ERR_MDTMDIR_UNSUPPORTED, NET_FTP_ERR_MDTM_FAILED, NET_FTP_ERR_DATEFORMAT_FAILED
  1011.      */
  1012.     
  1013.     function mdtm($file, $format = null)
  1014.     {
  1015.         $file = $this->_construct_path($file);
  1016.         if ($this->_check_dir($file)) {
  1017.             return $this->raiseError("Filename '$file' seems to be a directory.", NET_FTP_ERR_MDTMDIR_UNSUPPORTED);
  1018.         }
  1019.         $res = @ftp_mdtm($this->_handle, $file);
  1020.         if ($res == -1) {
  1021.             return $this->raiseError("Could not get last-modification-date of '$file'.", NET_FTP_ERR_MDTM_FAILED);
  1022.         }
  1023.         if (isset($format)) {
  1024.             $res = date($format, $res);
  1025.             if (!$res) {
  1026.                 return $this->raiseError("Date-format failed on timestamp '$res'.", NET_FTP_ERR_DATEFORMAT_FAILED);
  1027.             }
  1028.         }
  1029.         return $res;
  1030.     }
  1031.  
  1032.     /**
  1033.      * This will return the size of a given file in bytes. You can either give this function
  1034.      * a relative or an absolute file-path. NOTE: Some servers do not support this feature!
  1035.      *
  1036.      * @access  public
  1037.      * @param   string $file   The file to check
  1038.      * @return  mixed          Size in bytes or PEAR::Error
  1039.      * @see     NET_FTP_ERR_SIZE_FAILED
  1040.      */
  1041.     
  1042.     function size($file)
  1043.     {
  1044.         $file = $this->_construct_path($file);
  1045.         $res = @ftp_size($this->_handle, $file);
  1046.         if ($res == -1) {
  1047.             return $this->raiseError("Could not determine filesize of '$file'.", NET_FTP_ERR_SIZE_FAILED);
  1048.         } else {
  1049.             return $res;
  1050.         }
  1051.     }
  1052.  
  1053.     /**
  1054.      * This method returns a directory-list of the current directory or given one.
  1055.      * To display the current selected directory, simply set the first parameter to null
  1056.      * or leave it blank, if you do not want to use any other parameters.
  1057.      * <BR><BR>
  1058.      * There are 4 different modes of listing directories. Either to list only
  1059.      * the files (using NET_FTP_FILES_ONLY), to list only directories (using
  1060.      * NET_FTP_DIRS_ONLY) or to show both (using NET_FTP_DIRS_FILES, which is default).
  1061.      * <BR><BR>
  1062.      * The 4th one is the NET_FTP_RAWLIST, which returns just the array created by the
  1063.      * ftp_rawlist()-function build into PHP.
  1064.      * <BR><BR>
  1065.      * The other function-modes will return an array containing the requested data.
  1066.      * The files and dirs are listed in human-sorted order, but if you select
  1067.      * NET_FTP_DIRS_FILES the directories will be added above the files,
  1068.      * but although both sorted.
  1069.      * <BR><BR>
  1070.      * All elements in the arrays are associative arrays themselves. The have the following
  1071.      * structure:
  1072.      * <BR><BR>
  1073.      * Dirs:<BR>
  1074.      *           ["name"]        =>  string The name of the directory<BR>
  1075.      *           ["rights"]      =>  string The rights of the directory (in style "rwxr-xr-x")<BR>
  1076.      *           ["user"]        =>  string The owner of the directory<BR>
  1077.      *           ["group"]       =>  string The group-owner of the directory<BR>
  1078.      *           ["files_inside"]=>  string The number of files/dirs inside the directory
  1079.      *                                      excluding "." and ".."<BR>
  1080.      *           ["date"]        =>  int The creation-date as Unix timestamp<BR>
  1081.      *           ["is_dir"]      =>  bool true, cause this is a dir<BR>
  1082.      * <BR><BR>
  1083.      * Files:<BR>
  1084.      *           ["name"]        =>  string The name of the file<BR>
  1085.      *           ["size"]        =>  int Size in bytes<BR>
  1086.      *           ["rights"]      =>  string The rights of the file (in style "rwxr-xr-x")<BR>
  1087.      *           ["user"]        =>  string The owner of the file<BR>
  1088.      *           ["group"]       =>  string The group-owner of the file<BR>
  1089.      *           ["date"]        =>  int The creation-date as Unix timestamp<BR>
  1090.      *           ["is_dir"]      =>  bool false, cause this is a file<BR>
  1091.      *
  1092.      * @access  public
  1093.      * @param   string $dir   (optional) The directory to list or null, when listing the current directory.
  1094.      * @param   int    $mode  (optional) The mode which types to list (files, directories or both).
  1095.      * @return  mixed         The directory list as described above or PEAR::Error on failure.
  1096.      * @see     NET_FTP_DIRS_FILES, NET_FTP_DIRS_ONLY, NET_FTP_FILES_ONLY, NET_FTP_RAWLIST, NET_FTP_ERR_DETERMINEPATH_FAILED, NET_FTP_ERR_RAWDIRLIST_FAILED, NET_FTP_ERR_DIRLIST_UNSUPPORTED
  1097.      */
  1098.      
  1099.     function ls($dir = null, $mode = NET_FTP_DIRS_FILES)
  1100.     {
  1101.         if (!isset($dir)) {
  1102.             $dir = @ftp_pwd($this->_handle);
  1103.             if (!$dir) {
  1104.                 return $this->raiseError("Could not retrieve current directory", NET_FTP_ERR_DETERMINEPATH_FAILED);
  1105.             }
  1106.         }
  1107.         if (($mode != NET_FTP_FILES_ONLY) && ($mode != NET_FTP_DIRS_ONLY) && ($mode != NET_FTP_RAWLIST)) {
  1108.             $mode = NET_FTP_DIRS_FILES;
  1109.         }
  1110.  
  1111.         switch ($mode) {
  1112.             case NET_FTP_DIRS_FILES:    $res = $this->_ls_both ( $dir );
  1113.                                         break;
  1114.             case NET_FTP_DIRS_ONLY:     $res = $this->_ls_dirs ( $dir );
  1115.                                         break;
  1116.             case NET_FTP_FILES_ONLY:    $res = $this->_ls_files ( $dir );
  1117.                                         break;
  1118.             case NET_FTP_RAWLIST:       $res = @ftp_rawlist($this->_handle, $dir);
  1119.                                         break;
  1120.         }
  1121.  
  1122.         return $res;
  1123.     }
  1124.  
  1125.     /**
  1126.      * This method will delete the given file or directory ($path) from the server
  1127.      * (maybe recursive).
  1128.      *
  1129.      * Whether the given string is a file or directory is only determined by the last
  1130.      * sign inside the string ("/" or not).
  1131.      *
  1132.      * If you specify a directory, you can optionally specify $recursive as true,
  1133.      * to let the directory be deleted recursive (with all sub-directories and files
  1134.      * inherited).
  1135.      *
  1136.      * You can either give a absolute or relative path for the file / dir. If you choose to
  1137.      * use the relative path, it will be automatically completed with the actual
  1138.      * selected directory.
  1139.      *
  1140.      * @access  public
  1141.      * @param   string $path      The absolute or relative path to the file / directory.
  1142.      * @param   bool   $recursive (optional)
  1143.      * @return  mixed             True on success, otherwise PEAR::Error
  1144.      * @see     NET_FTP_ERR_DELETEFILE_FAILED, NET_FTP_ERR_DELETEDIR_FAILED, NET_FTP_ERR_REMOTEPATHNODIR
  1145.      */
  1146.     
  1147.     function rm($path, $recursive = false)
  1148.     {
  1149.         $path = $this->_construct_path($path);
  1150.  
  1151.         if ($this->_check_dir($path)) {
  1152.             if ($recursive) {
  1153.                 return $this->_rm_dir_recursive($path);
  1154.             } else {
  1155.                 return $this->_rm_dir($path);
  1156.             }
  1157.         } else {
  1158.             return $this->_rm_file($path);
  1159.         }
  1160.     }
  1161.  
  1162.     /**
  1163.      * This function will download a file from the ftp-server. You can either spcify a absolute
  1164.      * path to the file (beginning with "/") or a relative one, which will be completed
  1165.      * with the actual directory you selected on the server. You can specify
  1166.      * the path to which the file will be downloaded on the local
  1167.      * maschine, if the file should be overwritten if it exists (optionally, default is
  1168.      * no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
  1169.      * downloaded (if you do not specify this, the method tries to determine it automatically
  1170.      * from the mode-directory or uses the default-mode, set by you). If you give a relative
  1171.      * path to the local-file, the script-path is used as basepath.
  1172.      *
  1173.      * @access  public
  1174.      * @param   string $remote_file The absolute or relative path to the file to download
  1175.      * @param   string $local_file  The local file to put the downloaded in
  1176.      * @param   bool   $overwrite   (optional) Whether to overwrite existing file
  1177.      * @param   int    $mode        (optional) Either FTP_ASCII or FTP_BINARY
  1178.      * @return  mixed               True on success, otherwise PEAR::Error
  1179.      * @see     NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN, NET_FTP_ERR_OVERWRITELOCALFILE_FAILED, NET_FTP_ERR_OVERWRITELOCALFILE_FAILED
  1180.      */
  1181.     
  1182.     function get($remote_file, $local_file, $overwrite = false, $mode = null)
  1183.     {
  1184.         if (!isset($mode)) {
  1185.             $mode = $this->checkFileExtension($remote_file);
  1186.         }
  1187.  
  1188.         $remote_file = $this->_construct_path($remote_file);
  1189.  
  1190.         if (@file_exists($local_file) && !$overwrite) {
  1191.             return $this->raiseError("Local file '$local_file' exists and may not be overwriten.", NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN);
  1192.         }
  1193.         if (@file_exists($local_file) && !@is_writeable($local_file) && $overwrite) {
  1194.             return $this->raiseError("Local file '$local_file' is not writeable. Can not overwrite.", NET_FTP_ERR_OVERWRITELOCALFILE_FAILED);
  1195.         }
  1196.  
  1197.         if (@function_exists('ftp_nb_get')){
  1198.             $res = @ftp_nb_get($this->_handle, $local_file, $remote_file, $mode);
  1199.             while ($res == FTP_MOREDATA) {
  1200.                 $this->_announce('nb_get');
  1201.                 $res = @ftp_nb_continue ($this->_handle);
  1202.             }
  1203.         } else {
  1204.             $res = @ftp_get($this->_handle, $local_file, $remote_file, $mode);
  1205.         }
  1206.         if (!$res) {
  1207.             return $this->raiseError("File '$remote_file' could not be downloaded to '$local_file'.", NET_FTP_ERR_OVERWRITELOCALFILE_FAILED);
  1208.         } else {
  1209.             return true;
  1210.         }
  1211.     }
  1212.  
  1213.     /**
  1214.      * This function will upload a file to the ftp-server. You can either specify a absolute
  1215.      * path to the remote-file (beginning with "/") or a relative one, which will be completed
  1216.      * with the actual directory you selected on the server. You can specify
  1217.      * the path from which the file will be uploaded on the local
  1218.      * maschine, if the file should be overwritten if it exists (optionally, default is
  1219.      * no overwriting) and in which mode (FTP_ASCII or FTP_BINARY) the file should be
  1220.      * downloaded (if you do not specify this, the method tries to determine it automatically
  1221.      * from the mode-directory or uses the default-mode, set by you). If you give a relative
  1222.      * path to the local-file, the script-path is used as basepath.
  1223.      *
  1224.      * @access  public
  1225.      * @param   string $local_file  The local file to upload
  1226.      * @param   string $remote_file The absolute or relative path to the file to upload to
  1227.      * @param   bool   $overwrite   (optional) Whether to overwrite existing file
  1228.      * @param   int    $mode        (optional) Either FTP_ASCII or FTP_BINARY
  1229.      * @return  mixed               True on success, otherwise PEAR::Error
  1230.      * @see     NET_FTP_ERR_LOCALFILENOTEXIST, NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN, NET_FTP_ERR_UPLOADFILE_FAILED
  1231.      */
  1232.     
  1233.     function put($local_file, $remote_file, $overwrite = false, $mode = null)
  1234.     {
  1235.         if (!isset($mode)) {
  1236.             $mode = $this->checkFileExtension($local_file);
  1237.         }
  1238.         $remote_file = $this->_construct_path($remote_file);
  1239.  
  1240.         if (!@file_exists($local_file)) {
  1241.             return $this->raiseError("Local file '$local_file' does not exist.", NET_FTP_ERR_LOCALFILENOTEXIST);
  1242.         }
  1243.         if ((@ftp_size($this->_handle, $remote_file) != -1) && !$overwrite) {
  1244.             return $this->raiseError("Remote file '$remote_file' exists and may not be overwriten.", NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN);
  1245.         }
  1246.  
  1247.         if (function_exists('ftp_nb_put')){
  1248.             $res = @ftp_nb_put($this->_handle, $remote_file, $local_file, $mode);
  1249.             while ($res == FTP_MOREDATA) {
  1250.                 $this->_announce('nb_put');
  1251.                 $res = @ftp_nb_continue($this->_handle);
  1252.             }
  1253.  
  1254.         } else {
  1255.             $res = @ftp_put($this->_handle, $remote_file, $local_file, $mode);
  1256.         }
  1257.         if (!$res) {
  1258.             return $this->raiseError("File '$local_file' could not be uploaded to '$remote_file'.", NET_FTP_ERR_UPLOADFILE_FAILED);
  1259.         } else {
  1260.             return true;
  1261.         }
  1262.     }
  1263.  
  1264.     /**
  1265.      * This functionality allows you to transfer a whole directory-structure from the
  1266.      * remote-ftp to your local host. You have to give a remote-directory (ending with
  1267.      * '/') and the local directory (ending with '/') where to put the files you download.
  1268.      * The remote path is automatically completed with the current-remote-dir, if you give
  1269.      * a relative path to this function. You can give a relative path for the $local_path,
  1270.      * too. Then the script-basedir will be used for comletion of the path.
  1271.      * The parameter $overwrite will determine, whether to overwrite existing files or not.
  1272.      * Standard for this is false. Fourth you can explicitly set a mode for all transfer-
  1273.      * actions done. If you do not set this, the method tries to determine the transfer-
  1274.      * mode by checking your mode-directory for the file-extension. If the extension is not
  1275.      * inside the mode-directory, it will get your default-mode.
  1276.      *
  1277.      * @access  public
  1278.      * @param   string $remote_path The path to download
  1279.      * @param   string $local_path  The path to download to
  1280.      * @param   bool   $overwrite   (optional) Whether to overwrite existing files (true) or not (false, standard).
  1281.      * @param   int    $mode        (optional) The transfermode (either FTP_ASCII or FTP_BINARY).
  1282.      * @return  mixed               True on succes, otherwise PEAR::Error
  1283.      * @see     NET_FTP_ERR_OVERWRITELOCALFILE_FORBIDDEN, NET_FTP_ERR_OVERWRITELOCALFILE_FAILED, NET_FTP_ERR_OVERWRITELOCALFILE_FAILED, NET_FTP_ERR_REMOTEPATHNODIR, NET_FTP_ERR_LOCALPATHNODIR,NET_FTP_ERR_CREATELOCALDIR_FAILED
  1284.      */
  1285.     
  1286.     function getRecursive($remote_path, $local_path, $overwrite = false, $mode = null)
  1287.     {
  1288.         $remote_path = $this->_construct_path($remote_path);
  1289.         if (!$this->_check_dir($remote_path)) {
  1290.             return $this->raiseError("Given remote-path '$remote_path' seems not to be a directory.", NET_FTP_ERR_REMOTEPATHNODIR);
  1291.         }
  1292.         if (!$this->_check_dir($local_path)) {
  1293.             return $this->raiseError("Given local-path '$local_path' seems not to be a directory.", NET_FTP_ERR_LOCALPATHNODIR);
  1294.         }
  1295.  
  1296.         if (!@is_dir($local_path)) {
  1297.             $res = @mkdir($local_path);
  1298.             if (!$res) {
  1299.                 return $this->raiseError("Could not create dir '$local_path'", NET_FTP_ERR_CREATELOCALDIR_FAILED);
  1300.             }
  1301.         }
  1302.         $dir_list = array();
  1303.         $dir_list = $this->ls($remote_path, NET_FTP_DIRS_ONLY);
  1304.         foreach ($dir_list as $dir_entry) {
  1305.             if ($dir_entry['name'] != '.' && $dir_entry['name'] != '..') {
  1306.                 $remote_path_new = $remote_path.$dir_entry["name"]."/";
  1307.                 $local_path_new = $local_path.$dir_entry["name"]."/";
  1308.                 $result = $this->getRecursive($remote_path_new, $local_path_new, $overwrite, $mode);
  1309.                 if ($this->isError($result)) {
  1310.                     return $result;
  1311.                 }
  1312.             }
  1313.         }
  1314.         $file_list = array();
  1315.         $file_list = $this->ls($remote_path, NET_FTP_FILES_ONLY);
  1316.         foreach ($file_list as $file_entry) {
  1317.             $remote_file = $remote_path.$file_entry["name"];
  1318.             $local_file = $local_path.$file_entry["name"];
  1319.             $result = $this->get($remote_file, $local_file, $overwrite, $mode);
  1320.             if ($this->isError($result)) {
  1321.                 return $result;
  1322.             }
  1323.         }
  1324.         return true;
  1325.     }
  1326.  
  1327.     /**
  1328.      * This functionality allows you to transfer a whole directory-structure from your
  1329.      * local host to the remote-ftp. You have to give a remote-directory (ending with
  1330.      * '/') and the local directory (ending with '/') where to put the files you download.
  1331.      * The remote path is automatically completed with the current-remote-dir, if you give
  1332.      * a relative path to this function. You can give a relative path for the $local_path,
  1333.      * too. Then the script-basedir will be used for comletion of the path.
  1334.      * The parameter $overwrite will determine, whether to overwrite existing files or not.
  1335.      * Standard for this is false. Fourth you can explicitly set a mode for all transfer-
  1336.      * actions done. If you do not set this, the method tries to determine the transfer-
  1337.      * mode by checking your mode-directory for the file-extension. If the extension is not
  1338.      * inside the mode-directory, it will get your default-mode.
  1339.      *
  1340.      * @access  public
  1341.      * @param   string $remote_path The path to download 
  1342.      * @param   string $local_path  The path to download to
  1343.      * @param   bool   $overwrite   (optional) Whether to overwrite existing files (true) or not (false, standard).
  1344.      * @param   int    $mode        (optional) The transfermode (either FTP_ASCII or FTP_BINARY).
  1345.      * @return  mixed               True on succes, otherwise PEAR::Error
  1346.      * @see     NET_FTP_ERR_LOCALFILENOTEXIST, NET_FTP_ERR_OVERWRITEREMOTEFILE_FORBIDDEN, NET_FTP_ERR_UPLOADFILE_FAILED, NET_FTP_ERR_LOCALPATHNODIR, NET_FTP_ERR_REMOTEPATHNODIR
  1347.      */
  1348.     
  1349.     function putRecursive($local_path, $remote_path, $overwrite = false, $mode = null)
  1350.     {
  1351.         $remote_path = $this->_construct_path($remote_path);
  1352.         if (!$this->_check_dir($local_path) || !is_dir($local_path)) {
  1353.             return $this->raiseError("Given local-path '$local_path' seems not to be a directory.", NET_FTP_ERR_LOCALPATHNODIR);
  1354.         }
  1355.         if (!$this->_check_dir($remote_path)) {
  1356.             return $this->raiseError("Given remote-path '$remote_path' seems not to be a directory.", NET_FTP_ERR_REMOTEPATHNODIR);
  1357.         }
  1358.         $old_path = $this->pwd();
  1359.         if ($this->isError($this->cd($remote_path))) {
  1360.             $res = $this->mkdir($remote_path);
  1361.             if ($this->isError($res)) {
  1362.                 return $res;
  1363.             }
  1364.         }
  1365.         $this->cd($old_path);
  1366.         $dir_list = $this->_ls_local($local_path);
  1367.         foreach ($dir_list["dirs"] as $dir_entry) {
  1368.             $remote_path_new = $remote_path.$dir_entry."/";
  1369.             $local_path_new = $local_path.$dir_entry."/";
  1370.             $result = $this->putRecursive($local_path_new, $remote_path_new, $overwrite, $mode);
  1371.             if ($this->isError($result)) {
  1372.                 return $result;
  1373.             }
  1374.         }
  1375.  
  1376.         foreach ($dir_list["files"] as $file_entry) {
  1377.             $remote_file = $remote_path.$file_entry;
  1378.             $local_file = $local_path.$file_entry;
  1379.             $result = $this->put($local_file, $remote_file, $overwrite, $mode);
  1380.             if ($this->isError($result)) {
  1381.                 return $result;
  1382.             }
  1383.         }
  1384.         return true;
  1385.     }
  1386.  
  1387.     /**
  1388.      * This checks, whether a file should be transfered in ascii- or binary-mode
  1389.      * by it's file-extension. If the file-extension is not set or
  1390.      * the extension is not inside one of the extension-dirs, the actual set
  1391.      * transfer-mode is returned.
  1392.      *
  1393.      * @access  public
  1394.      * @param   string $filename  The filename to be checked
  1395.      * @return  int               Either FTP_ASCII or FTP_BINARY
  1396.      */
  1397.     
  1398.     function checkFileExtension($filename)
  1399.     {
  1400.         $pattern = "/\.(.*)$/";
  1401.         $has_extension = preg_match($pattern, $filename, $eregs);
  1402.         if (!$has_extension) {
  1403.             return $this->_mode;
  1404.         } else {
  1405.             $ext = $eregs[1];
  1406.         }
  1407.  
  1408.         if (!empty($this->_file_extensions[$ext])) {
  1409.             return $this->_file_extensions[$ext];
  1410.         }
  1411.  
  1412.         return $this->_mode;
  1413.     }
  1414.  
  1415.     /**
  1416.      * Set the Hostname
  1417.      *
  1418.      * @access  public
  1419.      * @param   string $host The Hostname to set
  1420.      * @return  bool True on success, otherwise PEAR::Error
  1421.      * @see     NET_FTP_ERR_HOSTNAMENOSTRING
  1422.      */
  1423.     
  1424.     function setHostname($host)
  1425.     {
  1426.         if (!is_string($host)) {
  1427.             return PEAR::raiseError("Hostname must be a string.", NET_FTP_ERR_HOSTNAMENOSTRING);
  1428.         }
  1429.         $this->_hostname = $host;
  1430.         return true;
  1431.     }
  1432.  
  1433.     /**
  1434.      * Set the Port
  1435.      *
  1436.      * @access  public
  1437.      * @param   int $port    The Port to set
  1438.      * @return  bool True on success, otherwise PEAR::Error
  1439.      * @see     NET_FTP_ERR_PORTLESSZERO
  1440.      */
  1441.     
  1442.     function setPort($port)
  1443.     {
  1444.         if (!is_int($port) || ($port < 0)) {
  1445.             PEAR::raiseError("Invalid port. Has to be integer >= 0", NET_FTP_ERR_PORTLESSZERO);
  1446.         }
  1447.         $this->_port = $port;
  1448.         return true;
  1449.     }
  1450.  
  1451.     /**
  1452.      * Set the Username
  1453.      *
  1454.      * @access  public
  1455.      * @param   string $user The Username to set
  1456.      * @return  mixed True on success, otherwise PEAR::Error
  1457.      * @see     NET_FTP_ERR_USERNAMENOSTRING
  1458.      */
  1459.     
  1460.     function setUsername($user)
  1461.     {
  1462.         if (empty($user) || !is_string($user)) {
  1463.             return PEAR::raiseError('Username $user invalid.', NET_FTP_ERR_USERNAMENOSTRING);
  1464.         }
  1465.         $this->_username = $user;
  1466.     }
  1467.  
  1468.     /**
  1469.      * Set the Password
  1470.      *
  1471.      * @access  private
  1472.      * @param   string $password  The Password to set
  1473.      * @return  void
  1474.      * @see     NET_FTP_ERR_PASSWORDNOSTRING
  1475.      */
  1476.     
  1477.     function setPassword($password)
  1478.     {
  1479.         if (empty($password) || !is_string($password)) {
  1480.             return PEAR::raiseError('Password xxx invalid.', NET_FTP_ERR_PASSWORDNOSTRING);
  1481.         }
  1482.         $this->_password = $password;
  1483.     }
  1484.  
  1485.     /**
  1486.      * Set the transfer-mode. You can use the predefined constants
  1487.      * FTP_ASCII or FTP_BINARY. The mode will be stored for any further transfers.
  1488.      *
  1489.      * @access  public
  1490.      * @param   int    $mode  The mode to set
  1491.      * @return  mixed         True on success, otherwise PEAR::Error
  1492.      * @see     NET_FTP_ERR_NOMODECONST
  1493.      */
  1494.     
  1495.     function setMode($mode)
  1496.     {
  1497.         if (($mode == FTP_ASCII) || ($mode == FTP_BINARY)) {
  1498.             $this->_mode = $mode;
  1499.             return true;
  1500.         } else {
  1501.             return $this->raiseError('FTP-Mode has either to be FTP_ASCII or FTP_BINARY', NET_FTP_ERR_NOMODECONST);
  1502.         }
  1503.     }
  1504.  
  1505.     /**
  1506.      * Set the transfer-method to passive mode
  1507.      *
  1508.      * @access  public
  1509.      * @return  void
  1510.      */
  1511.     
  1512.     function setPassive()
  1513.     {
  1514.         $this->_passv = true;
  1515.         @ftp_pasv($this->_handle, true);
  1516.     }
  1517.  
  1518.     /**
  1519.      * Set the transfer-method to active mode
  1520.      *
  1521.      * @access  public
  1522.      * @return  void
  1523.      */
  1524.     
  1525.     function setActive()
  1526.     {
  1527.         $this->_passv = false;
  1528.         @ftp_pasv($this->_handle, false);
  1529.     }
  1530.  
  1531.     /**
  1532.      * Set the timeout for FTP operations
  1533.      * Use this method to set a timeout for FTP operation. Timeout has to be an integer.
  1534.      *
  1535.      * @acess   public
  1536.      * @param   int $timeout the timeout to use
  1537.      * @return  bool True on success, otherwise PEAR::Error
  1538.      * @see     NET_FTP_ERR_TIMEOUTLESSZERO, NET_FTP_ERR_SETTIMEOUT_FAILED
  1539.      */
  1540.      
  1541.     function setTimeout ( $timeout = 0 ) 
  1542.     {
  1543.         if (!is_int($timeout) || ($timeout < 0)) {
  1544.             return PEAR::raiseError("Timeout $timeout is invalid, has to be an integer >= 0", NET_FTP_ERR_TIMEOUTLESSZERO);
  1545.         }
  1546.         $this->_timeout = $timeout;
  1547.         if (isset($this->_handle) && is_resource($this->_handle)) {
  1548.             $res = @ftp_set_option($this->_handle, FTP_TIMEOUT_SEC, $timeout);
  1549.         } else {
  1550.             $res = true;
  1551.         }
  1552.         if (!$res) {
  1553.             return PEAR::raiseError("Set timeout failed.", NET_FTP_ERR_SETTIMEOUT_FAILED);
  1554.         }
  1555.         return true;
  1556.     }        
  1557.     
  1558.     /**
  1559.      * Adds an extension to a mode-directory
  1560.      * The mode-directory saves file-extensions coresponding to filetypes
  1561.      * (ascii e.g.: 'php', 'txt', 'htm',...; binary e.g.: 'jpg', 'gif', 'exe',...).
  1562.      * The extensions have to be saved without the '.'. And
  1563.      * can be predefined in an external file (see: getExtensionsFile()).
  1564.      *
  1565.      * The array is build like this: 'php' => FTP_ASCII, 'png' => FTP_BINARY
  1566.      *
  1567.      * To change the mode of an extension, just add it again with the new mode!
  1568.      *
  1569.      * @access  public
  1570.      * @param   int    $mode  Either FTP_ASCII or FTP_BINARY
  1571.      * @param   string $ext   Extension
  1572.      * @return  void
  1573.      */
  1574.     
  1575.     function addExtension($mode, $ext)
  1576.     {
  1577.         $this->_file_extensions[$ext] = $mode;
  1578.     }
  1579.  
  1580.     /**
  1581.      * This function removes an extension from the mode-directories 
  1582.      * (described above).
  1583.      *
  1584.      * @access  public
  1585.      * @param   string $ext  The extension to remove
  1586.      * @return  void
  1587.      */
  1588.     
  1589.     function removeExtension($ext)
  1590.     {
  1591.         unset($this->_file_extensions[$ext]);
  1592.     }
  1593.  
  1594.     /**
  1595.      * This get's both (ascii- and binary-mode-directories) from the given file.
  1596.      * Beware, if you read a file into the mode-directory, all former set values 
  1597.      * will be unset!
  1598.      *
  1599.      * @access  public
  1600.      * @param   string $filename  The file to get from
  1601.      * @return  mixed             True on success, otherwise PEAR::Error
  1602.      * @see     NET_FTP_ERR_EXTFILENOTEXIST, NET_FTP_ERR_EXTFILEREAD_FAILED
  1603.      */
  1604.     
  1605.     function getExtensionsFile($filename)
  1606.     {
  1607.         if (!file_exists($filename)) {
  1608.             return $this->raiseError("Extensions-file '$filename' does not exist", NET_FTP_ERR_EXTFILENOTEXIST);
  1609.         }
  1610.  
  1611.         if (!is_readable($filename)) {
  1612.             return $this->raiseError("Extensions-file '$filename' is not readable", NET_FTP_ERR_EXTFILEREAD_FAILED);
  1613.         }
  1614.  
  1615.         $this->_file_extension = @parse_ini_file($filename);
  1616.         return true;
  1617.     }
  1618.  
  1619.     /**
  1620.      * Returns the Hostname
  1621.      *
  1622.      * @access  public
  1623.      * @return  string  The Hostname
  1624.      */
  1625.     
  1626.     function getHostname()
  1627.     {
  1628.         return $this->_hostname;
  1629.     }
  1630.  
  1631.     /**
  1632.      * Returns the Port
  1633.      *
  1634.      * @access  public
  1635.      * @return  int     The Port
  1636.      */
  1637.     
  1638.     function getPort()
  1639.     {
  1640.         return $this->_port;
  1641.     }
  1642.  
  1643.     /**
  1644.      * Returns the Username
  1645.      *
  1646.      * @access  public
  1647.      * @return  string  The Username
  1648.      */
  1649.     
  1650.     function getUsername()
  1651.     {
  1652.         return $this->_username;
  1653.     }
  1654.  
  1655.     /**
  1656.      * Returns the Password
  1657.      *
  1658.      * @access  public
  1659.      * @return  string  The Password
  1660.      */
  1661.     
  1662.     function getPassword()
  1663.     {
  1664.         return $this->_password;
  1665.     }
  1666.  
  1667.     /**
  1668.      * Returns the Transfermode
  1669.      *
  1670.      * @access  public
  1671.      * @return  int     The transfermode, either FTP_ASCII or FTP_BINARY.
  1672.      */
  1673.     
  1674.     function getMode()
  1675.     {
  1676.         return $this->_mode;
  1677.     }
  1678.  
  1679.     /**
  1680.      * Returns, whether the connection is set to passive mode or not
  1681.      *
  1682.      * @access  public
  1683.      * @return  bool    True if passive-, false if active-mode
  1684.      */
  1685.     
  1686.     function isPassive()
  1687.     {
  1688.         return $this->_passv;
  1689.     }
  1690.  
  1691.     /**
  1692.      * Returns the mode set for a file-extension
  1693.      *
  1694.      * @access  public
  1695.      * @param   string   $ext    The extension you wanna ask for
  1696.      * @return  int              Either FTP_ASCII, FTP_BINARY or NULL (if not set a mode for it)
  1697.      */
  1698.     
  1699.     function getExtensionMode($ext)
  1700.     {
  1701.         return @$this->_file_extensions[$ext];
  1702.     }
  1703.  
  1704.     /**
  1705.      * Get the currently set timeout.
  1706.      * Returns the actual timeout set.
  1707.      *
  1708.      * @access public
  1709.      * @return int The actual timeout
  1710.      */
  1711.     
  1712.     function getTimeout ( )
  1713.     {
  1714.         return ftp_get_option($this->_handle, FTP_TIMEOUT_SEC);
  1715.     }    
  1716.  
  1717.     /**
  1718.      * Adds a Net_FTP_Observer instance to the list of observers 
  1719.      * that are listening for messages emitted by this Net_FTP instance.
  1720.      *
  1721.      * @param   object   $observer     The Net_FTP_Observer instance to attach 
  1722.      *                                 as a listener.
  1723.      * @return  boolean                True if the observer is successfully attached.
  1724.      * @access  public
  1725.      * @since   1.3
  1726.      */
  1727.     
  1728.     function attach(&$observer)
  1729.     {
  1730.         if (!is_a($observer, 'Net_FTP_Observer')) {
  1731.             return false;
  1732.         }
  1733.  
  1734.         $this->_listeners[$observer->getId()] = &$observer;
  1735.         return true;
  1736.     }
  1737.  
  1738.     /**
  1739.      * Removes a Net_FTP_Observer instance from the list of observers.
  1740.      *
  1741.      * @param   object   $observer     The Net_FTP_Observer instance to detach 
  1742.      *                                 from the list of listeners.
  1743.      * @return  boolean                True if the observer is successfully detached.
  1744.      * @access  public
  1745.      * @since   1.3
  1746.      */
  1747.     
  1748.     function detach($observer)
  1749.     {
  1750.         if (!is_a($observer, 'Net_FTP_Observer') ||
  1751.             !isset($this->_listeners[$observer->getId()])) {
  1752.             return false;
  1753.         }
  1754.  
  1755.         unset($this->_listeners[$observer->getId()]);
  1756.         return true;
  1757.     }
  1758.  
  1759.     /**
  1760.      * Informs each registered observer instance that a new message has been
  1761.      * sent.                                                                
  1762.      *                                                                      
  1763.      * @param   mixed     $event       A hash describing the net event.   
  1764.      * @access  private                                                     
  1765.      * @since   1.3                                                         
  1766.      */                                                                     
  1767.     
  1768.     function _announce($event)
  1769.     {
  1770.         foreach ($this->_listeners as $id => $listener) {
  1771.             $this->_listeners[$id]->notify($event);
  1772.         }
  1773.     }
  1774.     
  1775.         /**
  1776.      * Rebuild the path, if given relative
  1777.      *
  1778.      * @access  private
  1779.      * @param   string $path   The path to check and construct
  1780.      * @return  string         The build path
  1781.      */
  1782.     
  1783.     function _construct_path($path)
  1784.     {
  1785.         if ((substr($path, 0, 1) != "/") && (substr($path, 0, 2) != "./")) {
  1786.             $actual_dir = @ftp_pwd($this->_handle);
  1787.             if (substr($actual_dir, (strlen($actual_dir) - 2), 1) != "/") {
  1788.                 $actual_dir .= "/";
  1789.             }
  1790.             $path = $actual_dir.$path;
  1791.         }
  1792.         return $path;
  1793.     }
  1794.  
  1795.     /**
  1796.      * Checks, whether a given string is a directory-path (ends with "/") or not.
  1797.      *
  1798.      * @access  private
  1799.      * @param   string $path  Path to check
  1800.      * @return  bool          True if $path is a directory, otherwise false
  1801.      */
  1802.     
  1803.     function _check_dir($path)
  1804.     {
  1805.         if (substr($path, (strlen($path) - 1), 1) == "/") {
  1806.             return true;
  1807.         } else {
  1808.             return false;
  1809.         }
  1810.     }
  1811.  
  1812.     /**
  1813.      * This will remove a file
  1814.      *
  1815.      * @access  private
  1816.      * @param   string $file   The file to delete
  1817.      * @return  mixed          True on success, otherwise PEAR::Error
  1818.      * @see     NET_FTP_ERR_DELETEFILE_FAILED
  1819.      */
  1820.     
  1821.     function _rm_file($file)
  1822.     {
  1823.         if (substr($file, 0, 1) == "/") {
  1824.             $res = @ftp_delete($this->_handle, $file);
  1825.         } else {
  1826.             $actual_dir = @ftp_pwd($this->_handle);
  1827.             if (substr($actual_dir, (strlen($actual_dir) - 2), 1) != "/") {
  1828.                 $actual_dir .= "/";
  1829.             }
  1830.             $file = $actual_dir.$file;
  1831.             $res = @ftp_delete($this->_handle, $file);
  1832.         }
  1833.  
  1834.         if (!$res) {
  1835.             return $this->raiseError("Could not delete file '$file'.", NET_FTP_ERR_DELETEFILE_FAILED);
  1836.         } else {
  1837.             return true;
  1838.         }
  1839.     }
  1840.  
  1841.     /**
  1842.      * This will remove a dir
  1843.      *
  1844.      * @access  private
  1845.      * @param   string $dir  The dir to delete
  1846.      * @return  mixed        True on success, otherwise PEAR::Error
  1847.      * @see     NET_FTP_ERR_REMOTEPATHNODIR, NET_FTP_ERR_DELETEDIR_FAILED
  1848.      */
  1849.     
  1850.     function _rm_dir($dir)
  1851.     {
  1852.         if (substr($dir, (strlen($dir) - 1), 1) != "/") {
  1853.             return $this->raiseError("Directory name '$dir' is invalid, has to end with '/'", NET_FTP_ERR_REMOTEPATHNODIR);
  1854.         }
  1855.         $res = @ftp_rmdir($this->_handle, $dir);
  1856.         if (!$res) {
  1857.             return $this->raiseError("Could not delete directory '$dir'.", NET_FTP_ERR_DELETEDIR_FAILED);
  1858.         } else {
  1859.             return true;
  1860.         }
  1861.     }
  1862.  
  1863.     /**
  1864.      * This will remove a dir and all subdirs and -files
  1865.      *
  1866.      * @access  private
  1867.      * @param   string $file  The dir to delete recursively
  1868.      * @return  mixed         True on success, otherwise PEAR::Error
  1869.      * @see     NET_FTP_ERR_REMOTEPATHNODIR, NET_FTP_ERR_DELETEDIR_FAILED
  1870.      */
  1871.     
  1872.     function _rm_dir_recursive($dir)
  1873.     {
  1874.         if (substr($dir, (strlen($dir) - 1), 1) != "/") {
  1875.             return $this->raiseError("Directory name '$dir' is invalid, has to end with '/'", NET_FTP_ERR_REMOTEPATHNODIR);
  1876.         }
  1877.         $file_list = $this->_ls_files($dir);
  1878.         foreach ($file_list as $file) {
  1879.             $file = $dir.$file["name"];
  1880.             $res = $this->rm($file);
  1881.             if ($this->isError($res)) {
  1882.                 return $res;
  1883.             }
  1884.         }
  1885.         $dir_list = $this->_ls_dirs($dir);
  1886.         foreach ($dir_list as $new_dir) {
  1887.             $new_dir = $dir.$new_dir["name"]."/";
  1888.             $res = $this->_rm_dir_recursive($new_dir);
  1889.             if ($this->isError($res)) {
  1890.                 return $res;
  1891.             }
  1892.         }
  1893.         $res = $this->_rm_dir($dir);
  1894.         if (!$res) {
  1895.             return $res;
  1896.         } else {
  1897.             return true;
  1898.         }
  1899.     }
  1900.  
  1901.     /**
  1902.      * Lists up files and directories
  1903.      *
  1904.      * @access  private
  1905.      * @param   string $dir  The directory to list up
  1906.      * @return  array        An array of dirs and files
  1907.      */
  1908.     
  1909.     function _ls_both($dir)
  1910.     {
  1911.         $list_splitted = $this->_list_and_parse($dir);
  1912.         if (!is_array($list_splitted["files"])) {
  1913.             $list_splitted["files"] = array();
  1914.         }
  1915.         if (!is_array($list_splitted["dirs"])) {
  1916.             $list_splitted["dirs"] = array();
  1917.         }
  1918.         $res = array();
  1919.         @array_splice($res, 0, 0, $list_splitted["files"]);
  1920.         @array_splice($res, 0, 0, $list_splitted["dirs"]);
  1921.         return $res;
  1922.     }
  1923.  
  1924.     /**
  1925.      * Lists up directories
  1926.      *
  1927.      * @access  private
  1928.      * @param   string $dir  The directory to list up
  1929.      * @return  array        An array of dirs
  1930.      */
  1931.     
  1932.     function _ls_dirs($dir)
  1933.     {
  1934.         $list["dirs"] = array();
  1935.         $list = $this->_list_and_parse($dir);
  1936.         return $list["dirs"];
  1937.     }
  1938.  
  1939.     /**
  1940.      * Lists up files
  1941.      *
  1942.      * @access  private
  1943.      * @param   string $dir  The directory to list up
  1944.      * @return  array        An array of files
  1945.      */
  1946.     
  1947.     function _ls_files($dir)
  1948.     {
  1949.         $list = $this->_list_and_parse($dir);
  1950.         if (!is_array($list["files"])) {
  1951.             $list["files"] = array();
  1952.         }
  1953.         return $list["files"];
  1954.     }
  1955.  
  1956.     /**
  1957.      * This lists up the directory-content and parses the items into well-formated arrays
  1958.      * The results of this array are sorted (dirs on top, sorted by name;
  1959.      * files below, sorted by name).
  1960.      *
  1961.      * @access  private
  1962.      * @param   string $dir  The directory to parse
  1963.      * @return  array        Lists of dirs and files
  1964.      * @see     NET_FTP_ERR_RAWDIRLIST_FAILED
  1965.      */
  1966.     
  1967.     function _list_and_parse($dir)
  1968.     {
  1969.         $dirs_list = array();
  1970.         $files_list = array();
  1971.         $dir_list = @ftp_rawlist($this->_handle, $dir);
  1972.         if ($dir_list === false) {
  1973.             return PEAR::raiseError('Could not get raw directory listing.', NET_FTP_ERR_RAWDIRLIST_FAILED);
  1974.         }
  1975.         if (!isset($this->_matcher)) {
  1976.             $this->_matcher = $this->_determine_os_match($dir_list);
  1977.             if (PEAR::isError($this->_matcher)) {
  1978.                 return $this->_matcher;
  1979.             }
  1980.         }
  1981.         foreach ($dir_list as $entry) {
  1982.             if (!preg_match($this->_matcher['pattern'], $entry, $m)) {
  1983.                 continue;
  1984.             }
  1985.             $entry = array();
  1986.             foreach ($this->_matcher['map'] as $key=>$val) {
  1987.                 $entry[$key] = $m[$val];
  1988.             }
  1989.             $entry['stamp'] = $this->_parse_Date($entry['date']);
  1990.  
  1991.             if ($entry['is_dir']) {
  1992.                 $dirs_list[] = $entry;
  1993.             } else {
  1994.                 $files_list[] = $entry;
  1995.             }
  1996.         }
  1997.         @usort($dirs_list, array("Net_FTP", "_nat_sort"));
  1998.         @usort($files_list, array("Net_FTP", "_nat_sort"));
  1999.         $res["dirs"] = $dirs_list;
  2000.         $res["files"] = $files_list;
  2001.         return $res;
  2002.     }
  2003.     
  2004.     /**
  2005.      * Determine server OS
  2006.      * This determines the server OS and returns a valid regex to parse
  2007.      * ls() output.
  2008.      *
  2009.      * @access  private
  2010.      * @param   array $dir_list The raw dir list to parse
  2011.      * @return  mixed An array of 'pattern' and 'map' on success, otherwise PEAR::Error
  2012.      * @see     NET_FTP_ERR_DIRLIST_UNSUPPORTED
  2013.      */
  2014.     
  2015.     function _determine_os_match(&$dir_list) {
  2016.     foreach ($dir_list as $entry) {
  2017.         foreach ($this->_ls_match as $os => $match) {
  2018.             if (preg_match($match['pattern'], $entry)) {
  2019.                     return $match;
  2020.                 }
  2021.             }
  2022.     }
  2023.         $error = 'The list style of your server seems not to be supported. Please email a "$ftp->ls(NET_FTP_RAWLIST);" output plus info on the server to the maintainer of this package to get it supported! Thanks for your help!';
  2024.         return PEAR::raiseError($error, NET_FTP_ERR_DIRLIST_UNSUPPORTED);
  2025.     }
  2026.     /**
  2027.      * Lists a local directory
  2028.      *
  2029.      * @access  private
  2030.      * @param   string $dir_path  The dir to list
  2031.      * @return  array             The list of dirs and files
  2032.      */
  2033.     
  2034.     function _ls_local($dir_path)
  2035.     {
  2036.         $dir = dir($dir_path);
  2037.         $dir_list = array();
  2038.         $file_list = array();
  2039.         while (false !== ($entry = $dir->read())) {
  2040.             if (($entry != '.') && ($entry != '..')) {
  2041.                 if (is_dir($dir_path.$entry)) {
  2042.                     $dir_list[] = $entry;
  2043.                 } else {
  2044.                     $file_list[] = $entry;
  2045.                 }
  2046.             }
  2047.         }
  2048.         $dir->close();
  2049.         $res['dirs'] = $dir_list;
  2050.         $res['files'] = $file_list;
  2051.         return $res;
  2052.     }
  2053.  
  2054.     /**
  2055.      * Function for use with usort().
  2056.      * Compares the list-array-elements by name.
  2057.      *
  2058.      * @access  private
  2059.      */
  2060.     
  2061.     function _nat_sort($item_1, $item_2)
  2062.     {
  2063.         return strnatcmp($item_1['name'], $item_2['name']);
  2064.     }
  2065.  
  2066.     /**
  2067.      * Parse dates to timestamps
  2068.      *
  2069.      * @access  private
  2070.      * @param   string $date  Date
  2071.      * @return  int           Timestamp
  2072.      * @see     NET_FTP_ERR_DATEFORMAT_FAILED
  2073.      */
  2074.     
  2075.     function _parse_Date($date)
  2076.     {
  2077.         // Sep 10 22:06 => Sep 10, <year> 22:06
  2078.         if (preg_match('/([A-Za-z]+)[ ]+([0-9]+)[ ]+([0-9]+):([0-9]+)/', $date, $res)) {
  2079.             $year = date('Y');
  2080.             $month = $res[1];
  2081.             $day = $res[2];
  2082.             $hour = $res[3];
  2083.             $minute = $res[4];
  2084.             $date = "$month $day, $year $hour:$minute";
  2085.             $tmpDate = strtotime($date);
  2086.             if ($tmpDate > time()) {
  2087.                 $year--;
  2088.                 $date = "$month $day, $year $hour:$minute";
  2089.             }
  2090.         }
  2091.         // 09-10-04 => 09/10/04
  2092.         elseif (preg_match('/^\d\d-\d\d-\d\d/',$date)) {
  2093.             $date = str_replace('-','/',$date);
  2094.         }
  2095.         $res = strtotime($date);
  2096.         if (!$res) {
  2097.             return $this->raiseError('Dateconversion failed.', NET_FTP_ERR_DATEFORMAT_FAILED);
  2098.         }
  2099.         return $res;
  2100.     }
  2101. }
  2102. ?>
  2103.